using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL.Fabric
{
    public class ALUBlock : Block 
    {
        ALUType eType;
        int iWidth = 0;
        int iShiftWidth = 0;
        bool bSingleInput = false;

        public ALUBlock(string psName, ALUType peType, int piWidth, int piShiftWidth)
            : base(psName,"ALU")
        {
            eType = peType;
            iWidth = piWidth;
            iShiftWidth = piShiftWidth;
            CreateIO();
        }

        public ALUBlock(string psName, ALUType peType, int piWidth)
            : base (psName,"ALU")
        {
            eType = peType;
            iWidth = piWidth;
            iShiftWidth = 0;
            CreateIO();            
        }

        private void CreateIO()
        {
            bool bTypeAdd=false;
            bool bTypeLogic=false;
            bool bBitOutput = false;
            bool bTypeCompare = false;
            bool bTypeShift = false;

            switch (eType)
            {
                case ALUType.Add:
                    bTypeAdd = true;
                    break;
                case ALUType.Inv:
                    bTypeLogic = true;
                    bSingleInput = true;
                    break;
                case ALUType.TwoComplement:
                    bTypeAdd = true;
                    break;
                case ALUType.AndMask:
                    bTypeLogic = true;
                    break;
                case ALUType.OrMask:
                    bTypeLogic = true;
                    break;
                case ALUType.XorMask:
                    bTypeLogic = true;
                    break;
                case ALUType.CompareGreater:
                    bTypeCompare = true;
                    bBitOutput = true;
                    break;
                case ALUType.CompareEqual:
                    bTypeCompare = true;
                    bBitOutput = true;
                    break;
                case ALUType.Subtract:
                    bTypeAdd = true;
                    break;
                case ALUType.AddSub:
                    bTypeAdd = true;
                    break;
                case ALUType.ShiftRightArith:
                    bTypeShift = true;
                    bSingleInput = true;
                    break;
                case ALUType.ShiftLeftArith:
                    bTypeShift = true;
                    bSingleInput = true;
                    break;
                case ALUType.ShiftRightLogical:
                    bTypeShift = true;
                    bSingleInput = true;
                    break;
                case ALUType.ShiftLeftLogical:
                    bTypeShift = true;
                    bSingleInput = true;
                    break;
                case ALUType.ShiftRightRotate:
                    bTypeShift = true;
                    bSingleInput = true;
                    break;
                case ALUType.ShiftLeftRotate:
                    bTypeShift = true;
                    bSingleInput = true;
                    break;
                case ALUType.Inc:
                    bTypeAdd = true;
                    bSingleInput = true;
                    break;
                case ALUType.Dec:
                    bTypeAdd = true;
                    bSingleInput = true;
                    break;
                default:
                    break;
            }

            if (bTypeShift)
            {
                if (iShiftWidth <= 0)
                    iShiftWidth = 1;
                else
                    if (iShiftWidth > iWidth)
                        throw new Exception("ALU shift width is greater than input width");
            }

            this.CreateInput("IN1", iWidth);
            if (!bSingleInput)
                this.CreateInput("IN2",iWidth);

            if (bBitOutput)
                this.CreateOutput("OUT",1);
            else
            {
                if (bTypeAdd)
                    this.CreateOutput("OUT", iWidth + 1);
                else
                    this.CreateOutput("OUT", iWidth);
            }

            if (eType == ALUType.AddSub)
            {
                this.CreateControlInput("SUB");
            }
        }

        internal override void GenerateStructure()
        {
            ALUComponent oALU = new ALUComponent(ParentComponent, "alu_" + sName, eType, iWidth, iShiftWidth);
            this.Inputs["IN1"].LinkNodeVector(oALU.I_A);
            if (!bSingleInput)
                this.Inputs["IN2"].LinkNodeVector(oALU.I_B);
            this.Outputs["OUT"].LinkNodeVector(oALU.O_A);

            if (eType == ALUType.AddSub)
                this.ControlInputs["SUB"].LinkNodeVector(oALU.I_Subtract);
        }
    }
}
